<?php

require_once 'RL/Interface.php';
require_once 'RL/Exception.php';
require_once 'includes/widgets.php';
require_once 'RL/UseScript.php';

class RL_SiteRequestHandler extends RL_UseScript
/**
 * Обработчик запроса от клиента (браузера)
 *
 * @package mumak
 * @subpackage core
 */
{
	// --- BasicRequestHandler --- {{{
	//
	/**
	 * Корневой URL проекта http://server/site/
	 *
	 * @var string
	 */
	public $base_url;

	/**
	 * Дирректоря сайта на сервере /site/
	 *
	 * @var string
	 */
	public $base_path;

	/**
	 * Базовая папка для файлов проекта
	 *
	 * @var string
	 */
	public $base_dir;

	/**
	 * Корневой URL сервера проекта (с портом, если не 80)
	 *
	 * @var string
	 */
	public $server_url;

	/**
	 * Корневой домен
	 *
	 * @var string
	 */
	public $base_domain;

	/**
	 * Текущий домен
	 *
	 * @var string
	 */
	public $current_domain;

	/**
	 * Домен для cookies
	 *
	 * @var string
	 */
	public $cookie_domain;

	/**
	 * Контекст приложения
	 *
	 * @var RL_Context
	 */
	public $ctx;


	// -- constructor -- {{{
	private function setPaths ()
	/**
	 * Устанавливает параметры директорий
	 *
	 * @return void
	 */
	{
		if (!isset($this->server_host) || !isset($this->server_port)) {
			$server = explode(':', $_SERVER['HTTP_HOST'], 2);
			if (count($server) == 2) {
				$server_host = $server[0];
				$server_port = intval($server[1]);
			} else {
				$server_host = $server[0];
				$server_port = 80;
			}
			if (!isset($this->server_host)) { $this->server_host = $server_host; }
			if (!isset($this->server_port)) { $this->server_port = $server_port; }
		}
		if (!isset($this->server_schema)) {
			$this->server_schema
				= strtolower(substr($_SERVER['SERVER_PROTOCOL'], 0, strpos($_SERVER['SERVER_PROTOCOL'], '/')));
		}
		// вычисляем base_url
		if (!isset($this->server_url) ) {
			$this->server_url =
				$this->server_schema . '://'.$this->server_host. ($this->server_port === 80 ? '' : ':'.$this->server_port);
		}
		if( !isset($this->base_path) ) {
			$this->base_path = url_dirname($_SERVER["PHP_SELF"]);
		}
		if( !isset($this->base_url) ) {
			$this->base_url =	$this->server_url . $this->base_path;
		}
		if( !isset($this->base_dir) ) {
			global $FU;
			$this->base_dir = $FU->dir->install;
		}
	}
	private function setDomains ()
	/**
	 * Устанавливает конфигурационные параметры доменов
	 *
	 * @return void
	 */
	{
		// устанавливаем домены
		if (!isset($this->base_domain)) {
			$this->base_domain = preg_replace("/^www\./i", "", $_SERVER["SERVER_NAME"]);
		}
		if (!isset($this->current_domain)) {
			$this->current_domain = preg_replace("/^www\./i", "", $_SERVER["HTTP_HOST"]);
		}
		if (!isset($this->cookie_domain)) {
			// lucky@npj: see http://ru.php.net/manual/ru/function.setcookie.php#49350
			// FIXME : 2008-01-23 : lucky
			// -- документировать --
			// когда $this->cookie_domain = false , то кука доступна лишь домену, с которого был запрос.
			// когда $this->cookie_domain содержит имя домена (с точкой в начале или без),
			// то кука отдается доменам верхних уровней.
			// для доменов типа 'localhost' $this->cookie_domain = false должен быть всегда.
			$this->cookie_domain = strpos($this->base_domain, '.') === false
				? false
				: // сделать куку доступной для всех поддоменов $this->base_domain
				"." .  $this->base_domain;
		}
		if (!isset($this->cookie_prefix)) {
			// по если не пусто, используем имя проекта (как универсальный префикс для всех имен).
			// иначе,
			// если $this->cookie_domain false, то $ths->cookie_prefix можно оставить пустым, т.к.
			// куки больше нигде не видны.
			// иначе,
			// используем имя хоста в качестве префикса.
			$this->cookie_prefix = $this->project_name ? $this->project_name :
				($this->cookie_domain ? strtr($this->base_domain, array('.' => '_')): '');
		}

		session_set_cookie_params(0, "/", $this->cookie_domain);
		// закончили с доменами
	}
	protected function createContext ()
	/**
	 * Создание рабочего окружения для сайта.
	 *
	 * @return RL_Context_Site
	 */
	{
		return RL_Context::createContext( $this , "Site" );
	}
	public function debugErrorHandler($errno, $errstr, $errfile, $errline, $errcontext = NULL, $error_types = NULL)
	/**
	 * Собственный обработчик ошибок.
	 *
	 * @param int $errno
	 * @param string $errstr
	 * @param string $errfile
	 * @param int $errline
	 * @param unknown_type $errcontext
	 * @param unknown_type $error_types
	 */
	{
		if ($errno & error_reporting()) {
			switch($errno) {
			default:
				$dump = debug_backtrace();
				array_shift($dump);
				echo sprintf('<div><b>Error: %s</b></div>', $errstr);
				echo sprintf('<div>%s:%s</div>', $errfile, $errline);
				echo sprintf('<p><b>backtrace</b></p>', $errfile, $errline);
				foreach ($dump as $k => $v) {
					echo sprintf('<p>%s%s%s</p>',
						(isset($v['class'])    ? $v['class']    : ''),
						(isset($v['type'])     ? $v['type']     : ''),
						(isset($v['function']) ? $v['function'] : '')
					);
					echo sprintf('<p>%s:%s</p>', $v['file'], $v['line']);
				}
				//				die();
			}
		}
	}


	final public function __construct( $config )
	/**
	 * Непереопределяемый конструктор
	 *
	 * @final
	 * @param mixed $config либо путь к файлу, либо объект
	 */
	{
		// устанавливаем наш обработчик ошибок
		//set_error_handler(array(&$this, 'debugErrorHandler'));

		if (@is_readable ( $config ) ) {
			$self = array();
			require_once($config);
			foreach ($self as $k=>$v) {
				$this->$k = $v;
			}
			$this->setPaths(); // устанавливаем параметры директорий

			$this->setDomains(); // устанавливаем домена

			//$this->fuckAllQuotes(); // удаляем кавычки
			$this->ENV = $_ENV;
			$this->SERVER = $_SERVER;

			// теперь тут создание рабочего окружения
			$this->useClass("RL_Context");
			$this->ctx = $this->createContext();
			if (isset($this->ctx->debug)) $this->debug = $this->ctx->debug;
		} else {
			error(__METHOD__.' Cannot read local configurations.');
		}
	}
	// -- constructor -- }}}


	public function error ( $message )
	/**
	 * Выкидыш ошибки
	 *
	 * @param string $message
	 */
	{
		trigger_error( $message , E_USER_ERROR );
	}

	//
	// --- BasicRequestHandler --- }}}

	// --- request handle workflow --- {{{

	public function initialize ()
	/**
	 * Пост-инициализация.
	 *
	 * @return void
	 */
	{
		// импортируем переменные окружения
		$this->GET     =& $_GET;
		$this->POST    =& $_POST;
		$this->FILES   =& $_FILES;
		$this->COOKIE  =& $_COOKIE;
		$this->REQUEST =& $_REQUEST;
		$this->SERVER  =& $_SERVER;

		$this->SERVER['URL_PATH'] = (current(
			explode(
				'?',
				$this->SERVER['REQUEST_URI'],
				2
			)));
		$this->SERVER['URL'] = $this->server_url.$this->SERVER['REQUEST_URI'];

		// инициализируем принципала
		if (isset($this->ctx->principal)) {
			$result = $this->ctx->principal->Identify();
			if ($result > PRINCIPAL_OK) {
				$this->ctx->principal->Guest();
			}
			debug("principal returned ". $result);
		}

		$this->ctx->tpl->setRef ( "SITE" , $this );

		// для setMessage
		$this->ui_message_cookie = $this->cookie_prefix.$this->ui_message_cookie;
	}

	public function handleRequest()
	/**
	 * Обработка пользовательского запроса.
	 *
	 * @return string
	 */
	{
		checkpoint(__METHOD__,'parse URL');

		// собственно запрос
		$this->useClass("RL_Request");
		$req = RL_Request::factory($this); // вернет экземлпяр RL_Request

		// тут req['_module'] может содержать название модуля
		// тут req['_action'] может содержать название хендлера
		// если их передали GET параметрами

		$req = $this->acceptRequest( $req ); // вернет array, прикольно да? :)

		// тут req['_module'] должен содержать название модуля
		// тут req['_action'] может содержать название хендлера
		if ((isset($req['is_ajax']) && $req['is_ajax'] == 1)
			|| isset($this->GET['cpaint_function'])
			|| isset($this->POST['cpaint_function'])
				) $protocol = 'cpaint';
			else $protocol = 'Http';

		$this->response = $this->buildResponse( $protocol );

		// здесь мы обрабатываем запрос
		$this->useClass("RL_Module");
		$this->useClass("RL_Handler");

		if ( isset($req["module"]) ) {
			checkpoint(__METHOD__,'create module');
			$this->ctx->debug->trace('[SiteRequestHandler] request module "'.$req['module'].'"');
			$uni_address = isset($req["path"]) ? $req['path'] : ''; // FIXME : 2007-08-02 : lucky

			// это жесткий хак
			// честно определять адрес модуля на сайте
			$module = RL_Module::create( $this , $req["module"], $uni_address);
			/* @var $module RL_Module */

			if ($module) {
				checkpoint(__METHOD__,'initialize module');
				$module->initialize( $req ); //поидее тут зарегистрируем функции cpaint

				$action   = $module->getMethod( $req ); // тут есл получать методы
				debug("Mapped ".$req["module"]." : ".$action);
				$this->ctx->debug->trace('[SiteRequestHandler] run action "'.$action.'"');
				checkpoint(__METHOD__,'execute module');

				$module->executeMethod( $action, (array) $req );
			}
		}

		if ($this->response->getStatus() === RL_NOTFOUND && !isset($this->response->content)) {
			$view = $this->views->findByName('404');
			$this->response->setContent( $view );
			$this->ctx->debug->error('[SiteRequestHandler] No reponse');
		}

		// положим лог событий на диск
		$this->ctx->logger->flushLog();

		checkpoint(__METHOD__,'send response');
		return $this->response->send();

	}

	/**
	 * Принять запрос, замапить урл
	 *
	 * @return array
	 **/
	public function acceptRequest($req)
	{
		foreach ($this->GET as $k=>$v) {
			$req[$k] = $v;
		}

		//#49 игногируем слеши на конце URL
		// FIXME : 2007-10-29 : lucky
		// в принципе, это не правильно: url должны быть однозачны
		// -- по хорошему надо делать редиректы.
		// Но для этого необходимо пофиксить генерацию ссылок
		// сейчас там полный бардак, и мы получим кучу бессмысленных редиров.. :(
		$this->SERVER['URL'] = rtrim($this->SERVER['URL'], '/');
		$req = $this->router->mapUrl($req, $this->SERVER['URL']);
		return $req;
	}
	public function buildResponse ( $protocol='Http' )
	{
		$this->useClass('RL_Response');
		$this->useClass('RL_Protocol');
		$r = new RL_Response($this);
		$r->setProtocol(RL_Protocol::create($this->ctx, $protocol));

		return $r;
	} // end of function buildResponse


	public function finalize ()
	/**
	 * Завершение работы RequestHandler'а
	 *
	 * @return void
	 */
	{
		return $this->end();
	}
	public function end()
	/**
	 * Штатное завершение работы.
	 *
	 * @return void
	 */
	{
		exit();
	}

	//
	// --- request handle workflow --- }}}

	function redirect( $href, $timeout=0)
	/**
	 * Редирект куда-нибудь (пусть пока здесь побудет)
	 *
	 * @param string $href
	 * @return void
	 */
	{
		if (headers_sent()) {
			$status = false;
		} else {
			if (strpos($href,"http://") !== 0) {
				$href = $this->ri->_host_prot . $href;
			}

			if ($timeout > 0) {
				header ('Refresh: ' . $timeout . '; URL="' . $href .'"');
				$status = true;
			} else {
				header ('Location: ' . $href);
				$status = $this->end(); // в реале никогда не возвращается, т.к. end() вызвает exit()
			}
		}
		return $status;
	}

	/**
	 * ui_message_cookie имя cookie, в которой передается текстовое сообщение о выполненном
	 * действии.
	 *
	 * @var string
	 * @access public
	 */
	var $ui_message_cookie = '_rhm';
	public function setMessage ($message)
	/**
	 * Установить сообщение о выполненном действии.
	 *
	 * сообщение в выполненном действии мы передаем в cookie с именем $this->ui_message_cookie
	 * браузер его ловит c помощью JavaScript и показывает юзеру.
	 */
	{
		setrawcookie($this->ui_message_cookie, rawurlencode($message), null,
			'/',
			$this->cookie_domain );
	} // end of method setMessage

	public function getPreferredLanguage ()
	{
		if (!isset($this->preferred_language)) {
			$this->useClass('RL_Language');
			$lang = isset($this->ctx->lang) ? $this->ctx->lang
			                                : 'ru';
			$this->preferred_language = RL_Language::create($this->ctx, $lang);
		}
		return $this->preferred_language;
	} // end of function getPreferredLanguage
	public function getPreferredOutputCharset ()
	{
		if (!isset($this->preferred_output_charset)) {
			$this->useClass('RL_Charset');
			$charset = isset($this->output_encoding) ? $this->output_encoding
			                                         : 'cp1251';
			$this->preferred_output_charset = RL_Charset::create($this->ctx, $charset);
		}
		return $this->preferred_output_charset;
	} // end of function getPreferredOutputCharset
}

